//--------------------------------------------------------------------------------------
// File: Tutorial02.fx
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------

struct Material
{
	float4 Ambient;
	float4 Diffuse;
	float4 Specular;
};
struct DirectionalLight
{
	float4 Ambient;
	float4 Diffuse;
	float4 Specular;
	float3 Direction;
	float pad;
};


Texture2D txDiffuse : register(t0);
SamplerState samLinear : register(s0);

Texture2D txNormal: register(t1);
//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
cbuffer CBViewProj : register(b0)
{
	matrix View;
	matrix Projection;
}

cbuffer CBPerFrame : register(b1)
{
	DirectionalLight gDirLight;
	float3 gEyePosW;
	float pad;
};

cbuffer CBPerObject : register(b2)
{
	matrix World;
	matrix WorldInvTrans;
	Material Mat;
}

cbuffer CBBoneTransforms: register(b3)
{
	//support max 96 bones
	float4x4 gBoneTransforms[96];
}

//--------------------------------------------------------------------------------------
struct VS_INPUT
{
	float3 Pos : POSITION;
	float3 Normal : NORMAL;
	float2 Tex : TEXCOORD0;
	float3 Tangent: TANGENT;
	float3 Weights    : WEIGHTS;
	uint4 BoneIndices : BONEINDICES;
};

struct PS_INPUT
{
	float4 Pos : SV_POSITION;
	float3 PosW : POSITION;
	float3 NormalW : NORMAL;
	float2 Tex : TEXCOORD0;
	float3 TangentW: TANGENT;
};

void ComputeDirectionalLight(Material mat, DirectionalLight L, float3 normal, float3 toEye,
	out float4 ambient, out float4 diffuse, out float4 specular)
{
	//initialize out
	ambient = float4(0.0, 0.0, 0.0, 0.0);
	diffuse = float4(0.0, 0.0, 0.0, 0.0);
	specular = float4(0.0, 0.0, 0.0, 0.0);

	// Add ambient term.
	ambient = mat.Ambient * L.Ambient;

	//the light vector aims opposite the direction the light rays travel
	float3 lightVec = -L.Direction;

	float diffuseFactor = dot(lightVec, normal);

	if (diffuseFactor > 0.0)
	{
		float3 v = reflect(-lightVec, normal);
		float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
		diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
		specular = specFactor * mat.Specular * L.Specular;
	}
}

float3 NormalSampleToWorldSpace(float3 normalSample, float3 normalW, float3 tangentW)
{
	float3 normalT = 2.0 * normalSample - 1.0f;
	float3 N = normalW;
	float3 T = normalize(tangentW - dot(tangentW, N) * N);
	float3 B = cross(N, T);
	float3x3 TBN = float3x3(T, B, N);
	float3 bumpedNormalW = mul(normalT, TBN);
	return bumpedNormalW;
}
//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT VS(VS_INPUT input)
{
	PS_INPUT output = (PS_INPUT)0;

	// Init array or else we get strange warnings about SV_POSITION.
	float weights[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
	weights[0] = input.Weights.x;
	weights[1] = input.Weights.y;
	weights[2] = input.Weights.z;
	weights[3] = 1.0f - weights[0] - weights[1] - weights[2];

	float3 posL = float3(0.0f, 0.0f, 0.0f);
	float3 normalL = float3(0.0f, 0.0f, 0.0f);
	float3 tangentL = float3(0.0f, 0.0f, 0.0f);
	for (int i = 0; i < 4; ++i)
	{
	// Assume no nonuniform scaling when transforming normals, so 
	// that we do not have to use the inverse-transpose.

		posL += weights[i] * mul(float4(input.Pos, 1.0f), gBoneTransforms[input.BoneIndices[i]]).xyz;
		normalL += weights[i] * mul(input.Normal, (float3x3)gBoneTransforms[input.BoneIndices[i]]);
		tangentL += weights[i] * mul(input.Tangent, (float3x3)gBoneTransforms[input.BoneIndices[i]]);
	}

	output.Pos = mul(float4(posL, 1.0f), World);
	output.PosW = output.Pos.xyz;
	output.Pos = mul(output.Pos, View);
	output.Pos = mul(output.Pos, Projection);

	output.NormalW = mul(normalL, (float3x3)WorldInvTrans);
	output.Tex = input.Tex;
	output.TangentW = mul(tangentL, (float3x3)World);
	return output;
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS(PS_INPUT input, uniform bool gAlphaClip) : SV_Target
{
	float3 toEyeW = normalize(gEyePosW - input.PosW);
	input.NormalW = normalize(input.NormalW);

	
	float4 texColor = float4(1.0f, 1.0f, 1.0f, 1.0f);
	texColor = txDiffuse.Sample(samLinear, input.Tex);
	
	//if (gAlphaClip)
	{
		//clip(texColor.a - 0.1f);
	}

	float3 normalMapSample = txNormal.Sample(samLinear, input.Tex);
	float3 bumpedNormalW = NormalSampleToWorldSpace(normalMapSample, input.NormalW, input.TangentW);


	
	// Start with a sum of zero.
	float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
	float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
	float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
	// Sum the light contribution from each light source.
	float4 A, D, S;
	ComputeDirectionalLight(Mat, gDirLight,
		bumpedNormalW, toEyeW, A, D, S);
	ambient += A;
	diffuse += D;
	spec += S;
	float4 litColor = ambient + diffuse + spec;
		// Common to take alpha from diffuse material.
		litColor.a = Mat.Diffuse.a;
	return  texColor * litColor;
}